package bbcom.control;

import bbcom.interfaces.*;

import java.io.File;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * Created by Thanh-Trung Pham.
 */
public class BBCoMSettings {
    private static final String SETTINGS_FILENAME = "BBCoMTool.properties";

    private static BBCoMSettings m_oBBCoMSettings;

    private String m_strImplementationParserPluginName;
    private IImplementationParser m_oImplementationParserPlugin;

    private String m_strUserInterfaceParserPluginName;
    private IUserInterfaceParser m_oUserInterfaceParserPlugin;

    private String m_strPropertiesParserPluginName;

    private IPropertiesParser m_oPropertiesParserPlugin;
    private String m_oScenarioParserPluginName;

    private IScenarioParser m_oScenarioParserPlugin;
    private String m_strDeployerPluginName;
    private String m_strContainerPluginName;

    private String m_strCurrentWorkingDirectory;
    private IDeployer m_oDeployerPlugin;

    /**
     * The constructor.
     *
     * @throws Exception if there is an error.
     */
    private BBCoMSettings() throws Exception {
        m_strCurrentWorkingDirectory = getCurrentWorkingDirectory();
        prepareClassLoader();
        initialize();
    }

    /**
     * Get the only instance.
     *
     * @return the only instance.
     * @throws Exception if there is an error.
     */
    public static synchronized BBCoMSettings getInstance() throws Exception {
        if (m_oBBCoMSettings == null) {
            m_oBBCoMSettings = new BBCoMSettings();
        }

        return m_oBBCoMSettings;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }

    /**
     * Initialize.
     *
     * @throws Exception if there is an error.
     */
    private void initialize() throws Exception {
        Properties oSettings = new Properties();
        oSettings.load(new FileInputStream(getCurrentWorkingDirectory() + File.separator + SETTINGS_FILENAME));

        m_strImplementationParserPluginName = oSettings.getProperty("implementation.parser.plugin");
        m_oImplementationParserPlugin = (IImplementationParser) Class.forName(m_strImplementationParserPluginName).newInstance();

        m_strUserInterfaceParserPluginName = oSettings.getProperty("userinterface.parser.plugin");
        m_oUserInterfaceParserPlugin = (IUserInterfaceParser) Class.forName(m_strUserInterfaceParserPluginName).newInstance();

        m_strPropertiesParserPluginName = oSettings.getProperty("properties.parser.plugin");
        m_oPropertiesParserPlugin = (IPropertiesParser) Class.forName(m_strPropertiesParserPluginName).newInstance();

        m_oScenarioParserPluginName = oSettings.getProperty("scenario.parser.plugin");
        m_oScenarioParserPlugin = (IScenarioParser) Class.forName(m_oScenarioParserPluginName).newInstance();

        m_strContainerPluginName = oSettings.getProperty("container.plugin");

        m_strDeployerPluginName = oSettings.getProperty("deployer.plugin");
        m_oDeployerPlugin = (IDeployer) Class.forName(m_strDeployerPluginName).newInstance();
    }

    /**
     * Prepare the class loader.
     *
     * @throws Exception if there is an error.
     */
    private void prepareClassLoader() throws Exception {
        List<URL> oURLs = new ArrayList<>();

        FilenameFilter oFilter = new FilenameFilter() {
            public boolean accept(File file, String name) {
                return name.endsWith(".jar");
            }
        };

        File oPluginFolder = new File(m_strCurrentWorkingDirectory + File.separator + "plugins");


        File[] oJarFiles = oPluginFolder.listFiles(oFilter);

        oURLs.add(oPluginFolder.toURI().toURL());
        for (int i = 0; i < oJarFiles.length; i++) {
            oURLs.add(oJarFiles[i].toURI().toURL());
        }

        URLClassLoader oSystemClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
        Class<URLClassLoader> oURLClassLoaderClass = URLClassLoader.class;
        Method oAddURLMethod = oURLClassLoaderClass.getDeclaredMethod("addURL", new Class[]{URL.class});
        oAddURLMethod.setAccessible(true);
        for (int i = 0; i < oURLs.size(); i++) {
            oAddURLMethod.invoke(oSystemClassLoader, new Object[]{oURLs.get(i)});
        }
    }

    /**
     * Get the current working directory.
     *
     * @return the current working directory.
     * @throws URISyntaxException if there is an error.
     */
    private String getCurrentWorkingDirectory() {
        String strCurrentWorkingDir = System.getProperty("user.dir");

        try {
            ProtectionDomain oProtectionDomain = getClass().getProtectionDomain();
            URL oLoadedLocation = oProtectionDomain.getCodeSource().getLocation();
            String strLoadedPath = oLoadedLocation.toURI().getSchemeSpecificPart();
            File oFile = new File(strLoadedPath);

            if (oFile.isFile()) {
                //loaded from a jar file
                return oFile.getParent();
            }

            return strCurrentWorkingDir;
        } catch (URISyntaxException oIgnoredURISyntaxException) {
            return strCurrentWorkingDir;
        }
    }

    public String getImplementationParserPluginName() {
        return m_strImplementationParserPluginName;
    }

    public IImplementationParser getImplementationParserPlugin() {
        return m_oImplementationParserPlugin;
    }

    public String getUserInterfaceParserPluginName() {
        return m_strUserInterfaceParserPluginName;
    }

    public String getPropertiesParserPluginName() {
        return m_strPropertiesParserPluginName;
    }

    public IPropertiesParser getPropertiesParserPlugin() {
        return m_oPropertiesParserPlugin;
    }


    public String getScenarioParserPluginName() {
        return m_oScenarioParserPluginName;
    }

    public IScenarioParser getScenarioParserPlugin() {
        return m_oScenarioParserPlugin;
    }

    public String getDeployerPluginName() {
        return m_strDeployerPluginName;
    }

    public IDeployer getDeployerPlugin() {
        return m_oDeployerPlugin;
    }


    public String getContainerPluginName() {
        return m_strContainerPluginName;
    }

    public IContainer getNewInstanceOfContainerPlugin() throws Exception {
        return (IContainer) Class.forName(m_strContainerPluginName).newInstance();
    }

    public IUserInterfaceParser getUserInterfaceParserPlugin() {
        return m_oUserInterfaceParserPlugin;
    }
}
